package naga.x.net.impl.pomelo;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import naga.x.App;
import naga.x.common.Config;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Packet;
import naga.x.net.codec.PacketDecoder;
import naga.x.net.impl.pomelo.body.request.HandshakeRequestBody;
import naga.x.net.impl.pomelo.body.response.HandshakeResponseBody;
import naga.x.net.impl.pomelo.config.Dictionary;
import naga.x.net.impl.pomelo.config.Protobuf;
import naga.x.net.impl.pomelo.protocol.Message;
import naga.x.net.impl.pomelo.protocol.Package;
import naga.x.util.NumberUtils;
import naga.x.util.SessionUtils;

public class PomeloDecoder extends ProtocolDecoderAdapter implements PacketDecoder {

	protected static final Logger LOG = LoggerFactory.getLogger(PomeloDecoder.class);
	
	@Override
	public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		Object buf = decode(session, in);
		if (buf != null) {
			out.write(buf);
		}
	}

	private static final int CODE_OK = 200;
	private static final int CODE_USE_ERROR = 500;
	private static final int CODE_OLD_CLIENT = 501;
	private void onHandshake(IoSession session, Package pkg) {
		HandshakeRequestBody req = JSONObject.parseObject(pkg.getBody(), HandshakeRequestBody.class);
		if (req.getSys() == null) {
			processError(session, CODE_USE_ERROR);
			return;
		}
//
//			  if(typeof this.checkClient === 'function') {
//			    if(!msg || !msg.sys || !this.checkClient(msg.sys.type, msg.sys.version)) {
//			      processError(socket, CODE_OLD_CLIENT);
//			      return;
//			    }
//			  }
//
		
		HandshakeResponseBody resp = new HandshakeResponseBody();
		resp.setSys(new HandshakeResponseBody.Sys());
//		resp.getSys().setHeartbeat(App.getApp().getConfig().getIntValue(Config.POMELO_HEARTBEAT));
		resp.getSys().setHeartbeat(App.getApp().getConfig().heartbeat);
		
//		if (App.getApp().getConfig().getBooleanValue(Config.POMELO_IS_USE_DICT)) {
		if (App.getApp().getConfig().useDict) {
			Dictionary dict = App.getApp().getConfig().getDictionary();
			if (!dict.getVersion().equals(req.getSys().getVersion())) {
				resp.getSys().setDict(dict.getDict());
				resp.getSys().setRouteToCode(dict.getDict());
				resp.getSys().setCodeToRoute(dict.getAbbrs());
			}
			resp.getSys().setUseDict(true);
		}
		
//		if (App.getApp().getConfig().getBooleanValue(Config.POMELO_IS_USE_PROTOBUF)) {
		if (App.getApp().getConfig().useProtobuf) {
//			Protobuf protobuf = App.getApp().getConfig().getObject(Config.POMELO_PROTOBUF, Protobuf.class);
			Protobuf protobuf = App.getApp().getConfig().protobuf;
			if (!protobuf.getVersion().equals(req.getSys().getProtoVersion())) {
//				resp.getSys().setProtos(null);
			}
			resp.getSys().setUseProto(true);
		}
//
//			  if(!!pomelo.app.components.__decodeIO__protobuf__) {
//			    if(!!this.useProtobuf) {
//			      throw new Error('protobuf can not be both used in the same project.');
//			    }
//			    var version = pomelo.app.components.__decodeIO__protobuf__.getVersion();
//			    if(!msg.sys.protoVersion || msg.sys.protoVersion < version) {
//			      opts.protos = pomelo.app.components.__decodeIO__protobuf__.getProtos();
//			    }
//			    opts.useProto = true;
//			  }
//
//			  if(this.useCrypto) {
//			    pomelo.app.components.__connector__.setPubKey(socket.id, msg.sys.rsa);
//			  }
//			  if(resp) {
//			    res.user = resp;
//			  }
		resp.setCode(CODE_OK);
		session.write(Package.encode(Package.TYPE_HANDSHAKE, JSON.toJSONBytes(resp)));
	}
	
	private static void processError(IoSession session, int errorCode) {
		session.write(Package.encode(Package.TYPE_HANDSHAKE, "{code:" + errorCode + "}"));
		session.closeOnFlush();
	}
	
	private void onHandshakeAck(IoSession session, Package pkg) {
		onHeartbeat(session, pkg);
	}
	
	private void onHeartbeat(IoSession session, Package pkg) {
		session.setAttribute(SessionUtils.LAST_HEARTBEAT_TIME, TimeUpdater.getInstance().now());
		session.write(Package.encode(Package.TYPE_HEARTBEAT, NumberUtils.EMPTY_BYTES));
	}
	
	private Object onData(IoSession session, Package pkg) {
		Message msg = Message.decode(pkg.getBody());
		Packet packet = msg.getPacket();
		return packet;
	}

	@Override
	public Object decode(IoSession session, IoBuffer in) {
		Package pkg = Package.decode(in);
		switch (pkg.getType()) {
			case Package.TYPE_HANDSHAKE:
				onHandshake(session, pkg);
				break;
			case Package.TYPE_HANDSHAKE_ACK:
				onHandshakeAck(session, pkg);
				break;
			case Package.TYPE_HEARTBEAT:
				onHeartbeat(session, pkg);
				break;
			case Package.TYPE_DATA:
				return onData(session, pkg);
			default:
				LOG.error("[POMELO DECODE]ERROR[PKG TYPE INVALID]" + pkg.getType());
				session.closeNow();
				break;
		}
		return null;
	}

}
